<?php

/**
 * 数据库访问类
 */
class Db
{
    private $field = '*';
    private $pdo = null;
    private $order = '';
    private $limit = 0;
    private $where = array(); //  防止不给where,先定义如果where不给值他就是一个空数组

    public function __construct()
    {
        $this->pdo = new PDO('mysql:host=mysql;dbname=myblog', 'root', 'root'); //  调用数据库
        $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    }
    // 指定表名称
    public function table($table)
    {
        $this->table = $table;
        return $this;
    }
    // 指定查询字段，所以就不用返回全部字段了只截取你想要的字段部分内容
    public function field($field)
    {
        $this->field = $field;
        return $this;
    }

    // 指定排序条件
    public function order($order)
    {
        $this->order = $order;
        return $this;
    }

    // 指定where条件
    public function where($where)
    {
        $this->where = $where;
        return $this;
    }

    // 查询数量
    public function limit($limit)
    {
        $this->limit = $limit;
        return $this;
    }

    // 分页
    public function pages($page, $pageSize = 10, $path = '/')
    {
        $count = $this->count(); // 总数
        $this->limit = ($page - 1) * $pageSize . ',' . $pageSize;
        $data = $this->lists();
        $pages = $this->_subpages($page, $pageSize, $count, $path);
        return array('total' => $count, 'data' => $data, 'pages' => $pages);
    }

    // 生成分页html(bootstrap风格);$cur_page:当前第几页,$pageSize:每页大小,$total:页面总数
    private function _subpages($cur_page, $pageSize, $total, $path)
    {
        $symbol = '?';
        $index = strpos($path, '?') > 0;
        if ($index != false && $index > 0) {
            $symbol = '&';
        }
        // 分页数（向上去取证）
        $html = '';
        $pages_count = ceil($total / $pageSize);
        // 生成首页，生成上一页
        if ($cur_page > 1) {
            $html .= "<li><a href='{$path}{$symbol}page=1'>首页</a></li>";
            $left_page = $cur_page - 1;
            $html .= "<li><a href='{$path}{$symbol}page={$left_page}'>上一页</a></li>";
        }
        // 生成数字页
        $start = $cur_page > ($pages_count - 3) ? ($pages_count - 3) : $cur_page; // 计算起始页,目的是当起始页到达末尾三页时不再增加，就用末尾第三页做起始页，不然会超出
        $start = $start - 3; // 给前面留空隙，才可以在14的时候可以看到11，12，13页
        $start = $start < 1 ? 1 : $start; // 判断起始页是否小于1，小于就让他为1，不是就为当前值
        $end = ($cur_page + 3) > $pages_count ? $pages_count : ($cur_page + 3);
        $end = $end > 7 ? $end : 7; // 判断结尾页是否大于7，不大于7说明没有7页，就定为7页
        $end = $pages_count >= 7 ? $end : $pages_count; //判断总页数是否大于或者等于7页，如果不是就把总页数作为页数
        for ($i = $start; $i <= $end; $i++) {
            $html .= $i == $cur_page ? "<li class='active'><a>{$i}</a></li>" : "<li><a href='{$path}{$symbol}page={$i}'>{$i}</a></li>";
        }
        // 生成下一页，生成尾页
        if ($cur_page < $pages_count) {
            $after_page = $cur_page + 1;
            $html .= "<li><a href='{$path}{$symbol}page={$after_page}'>下一页</a></li>";
            $html .= "<li><a href='{$path}{$symbol}page={$pages_count}'>尾页</a></li>";
        }
        // 渲染
        $html =  '<nav aria-label="Page navigation"><ul class="pagination">' . $html . '</ul></nav>';
        return $html;
    }
    // 查询数据总数
    public function count()
    {
        $sql = $this->_bulid_sql('count');
        $stmt = $this->pdo->prepare($sql); //  准备要执行的SQL语句
        $stmt->execute(); //  执行
        $total = $stmt->fetchColumn(0);
        return $total;
    }

    // 返回一条数据记录 
    public function item()
    {
        $sql = $this->_bulid_sql('select') . ' limit 1';
        $stmt = $this->pdo->prepare($sql); //  准备要执行的SQL语句
        $stmt->execute(); //  执行
        $res = $stmt->fetchAll(PDO::FETCH_ASSOC);
        return isset($res[0]) ? $res[0] : false;
    }
    //  返回多条数据记录
    public function lists()
    {
        $sql = $this->_bulid_sql('select');
        $stmt = $this->pdo->prepare($sql); //  准备要执行的SQL语句
        $stmt->execute(); //  执行
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    // 添加数据
    public function insert($data)
    {
        $sql = $this->_bulid_sql('insert', $data);
        $stmt = $this->pdo->prepare($sql); //  准备要执行的SQL语句
        $stmt->execute();   //  执行
        return $this->pdo->lastInsertId();
    }

    // 删除数据并且返回受影响的行数
    public function delete()
    {
        $sql = $this->_bulid_sql('delete');
        $stmt = $this->pdo->prepare(($sql)); //  准备要执行的SQL语句
        $stmt->execute(); //  执行
        return $stmt->rowCount();
    }

    // 更新数据
    public function update($data)
    {
        $sql = $this->_bulid_sql('update', $data);
        $stmt = $this->pdo->prepare(($sql)); //  准备要执行的SQL语句
        $stmt->execute(); //  执行
        return $stmt->rowCount();
    }

    //  构造sql语句
    private function _bulid_sql($type, $data = null)
    {
        $sql = '';
        // 查询操作
        if ($type == 'select') {
            $where = $this->_bulid_where();
            $sql = "select {$this->field} from {$this->table} {$where}";
            if ($this->order) { // 正反排序
                $sql .= " order by {$this->order}";
            }
            if ($this->limit) {
                $sql .= " limit {$this->limit}";
            }
        }
        // count的处理方法，避免*直接查询整个数据库
        if ($type == 'count') {
            $where = $this->_bulid_where();
            $field_list = explode(',', $this->field);
            $field = count($field_list) > 0 ? '*' : $this->field;
            $sql = "select count({$field}) from {$this->table} {$where}";
        }
        // 插入操作
        if ($type == 'insert') {
            $sql = "insert into {$this->table}";
            $fields = $values = array();
            foreach ($data as $key => $val) {
                $fields[] = "`" . $key . "`";
                $values[] = is_string($val) ? "'" . $val . "'" : $val;
            }
            // implode把数组元素组合为字符串
            $sql .= "(" . implode(',', $fields) . ")values(" . implode(',', $values) . ")";
        }
        // 删除操作
        if ($type == 'delete') {
            // $sql="delete from demo_article where id=5";
            $where = $this->_bulid_where();
            $sql = "delete from {$this->table} {$where}";
        }
        // 更新操作
        if ($type == 'update') {
            // 生成where条件
            $where = $this->_bulid_where();
            // 封装set
            $str = '';
            foreach ($data as $key => $val) {
                $val = is_string($val) ? "'" . $val . "'" : $val;
                $str .= "{$key}={$val}";
            }
            $str = $str ? " set {$str}" : '';
            $sql = "update {$this->table} {$str} {$where}";
        }
        return $sql;
    }

    // 组装where条件字符串
    private function _bulid_where()
    {
        $where = '';
        if (is_array($this->where)) {
            //  数组执行方式
            foreach ($this->where as $key => $value) {
                $value = is_string($value) ? "'" . $value . "'" : $value;
                $where .= "`{$key}`={$value} and";
            }
        } else { //  字符串执行方式
            $where = $this->where;
        }
        $where = rtrim($where, 'and'); //  rtrim移除函数右边的字符或者空白字符
        $where = $where == '' ? '' : "where {$where}"; //  如果where是空值就让他为空,不是的话再where值前面加上where

        return $where;
    }
}
